using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using WCSFContrib.Extensions.CompositeWeb.Tests.Mocks;
using WCSFContrib.Extensions.CompositeWeb.Configuration;
using WCSFContrib.Extensions.CompositeWeb.Services;
using Microsoft.Practices.CompositeWeb.Interfaces;
using Microsoft.Practices.CompositeWeb.Configuration;
using System.Configuration;
using WCSFContrib.Extensions.CompositeWeb.Interfaces;

namespace WCSFContrib.Extensions.CompositeWeb.Tests.Services
{
    [TestClass]
    public class WebModuleEnumeratorFixture
    {
        [TestMethod]
        public void EnumeratesOneModule()
        {
            MockModuleInfoStore store = new MockModuleInfoStore();
            store.Modules = new CustomModuleConfigurationElement[] { new CustomModuleConfigurationElement("Module1.Name", "Module1.Assembly", "Module1.VirtualPath") };
            WebModuleEnumerator enumerator = new WebModuleEnumerator(store);

            IModuleInfo[] modules = enumerator.EnumerateModules();

            Assert.AreEqual(1, modules.Length);
            Assert.AreEqual("Module1.Name", modules[0].Name);
            Assert.AreEqual("Module1.Assembly", modules[0].AssemblyName);
            Assert.AreEqual("Module1.VirtualPath", modules[0].VirtualPath);
        }

        [TestMethod]
        public void EnumeratesOneModuleWithoutServices()
        {
            MockModuleInfoStore store = new MockModuleInfoStore();
            store.Modules = new CustomModuleConfigurationElement[] { new CustomModuleConfigurationElement("Module1.Name", "Module1.Assembly", "Module1.VirtualPath") };
            WebModuleEnumerator enumerator = new WebModuleEnumerator(store);

            CustomDependantModuleInfo[] modules = enumerator.EnumerateModules() as CustomDependantModuleInfo[];

            Assert.AreEqual(1, modules.Length);
            Assert.AreEqual("Module1.Name", modules[0].Name);
            Assert.AreEqual("Module1.Assembly", modules[0].AssemblyName);
            Assert.AreEqual("Module1.VirtualPath", modules[0].VirtualPath);
            Assert.IsNull(modules[0].Services);
        }

        [TestMethod]
        public void EnumeratesOneModuleWithOneService()
        {
            MockModuleInfoStore store = new MockModuleInfoStore();
            CustomModuleConfigurationElement moduleElement = new CustomModuleConfigurationElement("Module1.Name", "Module1.Assembly", "Module1.VirtualPath");
            moduleElement.Services.Add(new ServiceConfigurationElement(typeof(IServiceLoader), typeof(ServiceLoader), "Global"));
            store.Modules = new CustomModuleConfigurationElement[] { moduleElement };
            WebModuleEnumerator enumerator = new WebModuleEnumerator(store);

            CustomDependantModuleInfo[] modules = enumerator.EnumerateModules() as CustomDependantModuleInfo[];

            Assert.AreEqual(1, modules.Length);
            Assert.AreEqual("Module1.Name", modules[0].Name);
            Assert.AreEqual("Module1.Assembly", modules[0].AssemblyName);
            Assert.AreEqual("Module1.VirtualPath", modules[0].VirtualPath);
            Assert.AreEqual(1, modules[0].Services.Length);
            Assert.AreEqual(typeof(IServiceLoader), modules[0].Services[0].RegisterAs);
            Assert.AreEqual(typeof(ServiceLoader), modules[0].Services[0].Type);
            Assert.AreEqual(ServiceScope.Global, modules[0].Services[0].Scope);
        }

        [TestMethod]
        [ExpectedException(typeof(ArgumentException))]
        public void ShouldFailIfScopeNotExists()
        {
            MockModuleInfoStore store = new MockModuleInfoStore();
            CustomModuleConfigurationElement moduleElement = new CustomModuleConfigurationElement("Module1.Name", "Module1.Assembly", "Module1.VirtualPath");
            moduleElement.Services.Add(new ServiceConfigurationElement(typeof(IServiceLoader), typeof(ServiceLoader), "Any"));
            store.Modules = new CustomModuleConfigurationElement[] { moduleElement };
            WebModuleEnumerator enumerator = new WebModuleEnumerator(store);

            CustomDependantModuleInfo[] modules = enumerator.EnumerateModules() as CustomDependantModuleInfo[];
        }


        [TestMethod]
        public void EnumeratesOneModuleWithTwoServices()
        {
            MockModuleInfoStore store = new MockModuleInfoStore();
            CustomModuleConfigurationElement moduleElement = new CustomModuleConfigurationElement("Module1.Name", "Module1.Assembly", "Module1.VirtualPath");
            moduleElement.Services.Add(new ServiceConfigurationElement(typeof(IServiceLoader), typeof(ServiceLoader), "Global"));
            moduleElement.Services.Add(new ServiceConfigurationElement(typeof(ICustomModuleInfoStore), typeof(WebConfigModuleInfoStore), "Module"));
            store.Modules = new CustomModuleConfigurationElement[] { moduleElement };
            WebModuleEnumerator enumerator = new WebModuleEnumerator(store);

            CustomDependantModuleInfo[] modules = enumerator.EnumerateModules() as CustomDependantModuleInfo[];

            Assert.AreEqual(1, modules.Length);
            Assert.AreEqual("Module1.Name", modules[0].Name);
            Assert.AreEqual("Module1.Assembly", modules[0].AssemblyName);
            Assert.AreEqual("Module1.VirtualPath", modules[0].VirtualPath);
            Assert.AreEqual(2, modules[0].Services.Length);
            Assert.AreEqual(typeof(IServiceLoader), modules[0].Services[0].RegisterAs);
            Assert.AreEqual(typeof(ServiceLoader), modules[0].Services[0].Type);
            Assert.AreEqual(ServiceScope.Global, modules[0].Services[0].Scope);
            Assert.AreEqual(typeof(ICustomModuleInfoStore), modules[0].Services[1].RegisterAs);
            Assert.AreEqual(typeof(WebConfigModuleInfoStore), modules[0].Services[1].Type);
            Assert.AreEqual(ServiceScope.Module, modules[0].Services[1].Scope);
        }

        [TestMethod]
        public void EnumeratesOneModuleWithoutVirtualPath()
        {
            MockModuleInfoStore store = new MockModuleInfoStore();
            store.Modules = new CustomModuleConfigurationElement[] { new CustomModuleConfigurationElement("Module1.Name", "Module1.Assembly", null) };
            WebModuleEnumerator enumerator = new WebModuleEnumerator(store);

            IModuleInfo[] modules = enumerator.EnumerateModules();

            Assert.AreEqual(1, modules.Length);
            Assert.AreEqual("Module1.Name", modules[0].Name);
            Assert.AreEqual("Module1.Assembly", modules[0].AssemblyName);
            Assert.AreEqual(null, modules[0].VirtualPath);
        }

        [TestMethod]
        public void Enumerates0Modules()
        {
            WebModuleEnumerator enumerator = new WebModuleEnumerator(new MockModuleInfoStore());

            IModuleInfo[] modules = enumerator.EnumerateModules();

            Assert.AreEqual(0, modules.Length);
        }

        [TestMethod]
        public void EnumeratesThreeModulesWithDependencies()
        {
            MockModuleInfoStore store = new MockModuleInfoStore();
            CustomModuleConfigurationElement module1 = new CustomModuleConfigurationElement("Module1.Name", "Module1.Assembly", "Module1.VirtualPath");
            module1.Dependencies = new ModuleDependencyCollection(new ModuleDependencyConfigurationElement[] { new ModuleDependencyConfigurationElement("Module2.Name") });
            CustomModuleConfigurationElement module2 = new CustomModuleConfigurationElement("Module2.Name", "Module2.Assembly", "Module2.VirtualPath");
            module2.Dependencies = new ModuleDependencyCollection(new ModuleDependencyConfigurationElement[] { new ModuleDependencyConfigurationElement("Module3.Name") });
            CustomModuleConfigurationElement module3 = new CustomModuleConfigurationElement("Module3.Name", "Module3.Assembly", "Module3.VirtualPath");
            store.Modules = new CustomModuleConfigurationElement[] { module3, module2, module1 };
            WebModuleEnumerator enumerator = new WebModuleEnumerator(store);

            List<IModuleInfo> modules = new List<IModuleInfo>(enumerator.EnumerateModules());

            Assert.AreEqual(3, modules.Count);
            Assert.IsTrue(modules.Exists(delegate(IModuleInfo module) { return module.Name == "Module1.Name"; }));
            Assert.IsTrue(modules.Exists(delegate(IModuleInfo module) { return module.Name == "Module2.Name"; }));
            Assert.IsTrue(modules.Exists(delegate(IModuleInfo module) { return module.Name == "Module3.Name"; }));
        }

        [TestMethod]
        public void EnumeratesThreeModulesWithoutDependenciesInCorrectOrder()
        {
            MockModuleInfoStore store = new MockModuleInfoStore();
            CustomModuleConfigurationElement module1 = new CustomModuleConfigurationElement("Module1.Name", "Module1.Assembly", "Module1.VirtualPath");
            CustomModuleConfigurationElement module2 = new CustomModuleConfigurationElement("Module2.Name", "Module2.Assembly", "Module2.VirtualPath");
            CustomModuleConfigurationElement module3 = new CustomModuleConfigurationElement("Module3.Name", "Module3.Assembly", "Module3.VirtualPath");
            store.Modules = new CustomModuleConfigurationElement[] { module3, module2, module1 };
            WebModuleEnumerator enumerator = new WebModuleEnumerator(store);

            IModuleInfo[] modules = enumerator.EnumerateModules();

            Assert.AreEqual(3, modules.Length);
            Assert.AreEqual("Module1.Name", modules[0].Name);
            Assert.AreEqual("Module2.Name", modules[1].Name);
            Assert.AreEqual("Module3.Name", modules[2].Name);
        }

        [TestMethod]
        public void EnumeratesThreeModulesWithoutDependenciesInCorrectOrderWithServices()
        {
            MockModuleInfoStore store = new MockModuleInfoStore();
            CustomModuleConfigurationElement module1 = new CustomModuleConfigurationElement("Module1.Name", "Module1.Assembly", "Module1.VirtualPath");
            module1.Services.Add(new ServiceConfigurationElement(typeof(IServiceLoader), typeof(ServiceLoader), "Global"));
            CustomModuleConfigurationElement module2 = new CustomModuleConfigurationElement("Module2.Name", "Module2.Assembly", "Module2.VirtualPath");
            module2.Services.Add(new ServiceConfigurationElement(typeof(ICustomModuleInfoStore), typeof(WebConfigModuleInfoStore), "Module"));
            CustomModuleConfigurationElement module3 = new CustomModuleConfigurationElement("Module3.Name", "Module3.Assembly", "Module3.VirtualPath");
            store.Modules = new CustomModuleConfigurationElement[] { module3, module2, module1 };
            WebModuleEnumerator enumerator = new WebModuleEnumerator(store);

            CustomDependantModuleInfo[] modules = enumerator.EnumerateModules() as CustomDependantModuleInfo[];

            Assert.AreEqual(3, modules.Length);
            Assert.AreEqual("Module1.Name", modules[0].Name);
            Assert.AreEqual("Module2.Name", modules[1].Name);
            

            Assert.AreEqual(1, modules[0].Services.Length);
            Assert.AreEqual(typeof(IServiceLoader), modules[0].Services[0].RegisterAs);
            Assert.AreEqual(typeof(ServiceLoader), modules[0].Services[0].Type);
            Assert.AreEqual(ServiceScope.Global, modules[0].Services[0].Scope);

            Assert.AreEqual(1, modules[1].Services.Length);
            Assert.AreEqual(typeof(ICustomModuleInfoStore), modules[1].Services[0].RegisterAs);
            Assert.AreEqual(typeof(WebConfigModuleInfoStore), modules[1].Services[0].Type);
            Assert.AreEqual(ServiceScope.Module, modules[1].Services[0].Scope);

            Assert.AreEqual("Module3.Name", modules[2].Name);
        }

        [TestMethod]
        public void EnumeratesTwoModulesWithoutVirtualPath()
        {
            MockModuleInfoStore store = new MockModuleInfoStore();
            CustomModuleConfigurationElement module1 = new CustomModuleConfigurationElement("Module1.Name", "Module1.Assembly", null);
            CustomModuleConfigurationElement module2 = new CustomModuleConfigurationElement("Module2.Name", "Module2.Assembly", string.Empty);
            store.Modules = new CustomModuleConfigurationElement[] { module2, module1 };
            WebModuleEnumerator enumerator = new WebModuleEnumerator(store);

            IModuleInfo[] modules = enumerator.EnumerateModules();

            Assert.AreEqual(2, modules.Length);
            Assert.AreEqual("Module1.Name", modules[0].Name);
            Assert.AreEqual("Module2.Name", modules[1].Name);
        }

        [TestMethod]
        [ExpectedException(typeof(ConfigurationErrorsException))]
        public void EnumerateThrowsIfDuplicateModuleVirtualPaths()
        {
            MockModuleInfoStore store = new MockModuleInfoStore();
            CustomModuleConfigurationElement module1 = new CustomModuleConfigurationElement("Module1.Name", "Module1.Assembly", "Module1.VirtualPath");
            CustomModuleConfigurationElement module2 = new CustomModuleConfigurationElement("Module2.Name", "Module2.Assembly", "Module1.VirtualPath");
            store.Modules = new CustomModuleConfigurationElement[] { module2, module1 };
            WebModuleEnumerator enumerator = new WebModuleEnumerator(store);

            IModuleInfo[] modules = enumerator.EnumerateModules();
        }

        [TestMethod]
        [ExpectedException(typeof(ConfigurationErrorsException))]
        public void EnumerateThrowsIfDuplicateNames()
        {
            MockModuleInfoStore store = new MockModuleInfoStore();
            CustomModuleConfigurationElement module1 = new CustomModuleConfigurationElement("Module1.Name", "Module1.Assembly", "Module1.VirtualPath");
            CustomModuleConfigurationElement module2 = new CustomModuleConfigurationElement("Module1.Name", "Module2.Assembly", "Module2.VirtualPath");
            store.Modules = new CustomModuleConfigurationElement[] { module2, module1 };
            WebModuleEnumerator enumerator = new WebModuleEnumerator(store);

            IModuleInfo[] modules = enumerator.EnumerateModules();
        }

        [TestMethod]
        [ExpectedException(typeof(ConfigurationErrorsException))]
        public void EnumerateThrowsIfDuplicateAssemblyNames()
        {
            MockModuleInfoStore store = new MockModuleInfoStore();
            CustomModuleConfigurationElement module1 = new CustomModuleConfigurationElement("Module1.Name", "Module1.Assembly", "Module1.VirtualPath");
            CustomModuleConfigurationElement module2 = new CustomModuleConfigurationElement("Module2.Name", "Module1.Assembly", "Module2.VirtualPath");
            store.Modules = new CustomModuleConfigurationElement[] { module2, module1 };
            WebModuleEnumerator enumerator = new WebModuleEnumerator(store);

            IModuleInfo[] modules = enumerator.EnumerateModules();
        }
    }
}